# 通过凸包、凸缺陷检测实现手势识别
# 凸包定义：物体最外层凸多边形(能够完全包围住轮廓)

import cv2
import numpy

# 计算三角形面积(向量叉乘)
def triangle_area(p1, p2, p3)->float:
    return 0.5 * numpy.abs(
        (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p3[0] - p1[0]) * (p2[1] - p1[1])
    )

image_bgr = cv2.imread("images/hand.png")
if image_bgr is None:
    exit(1)

image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
cv2.imshow("image_gray", image_gray)

threshold_ret, image_binary = cv2.threshold(image_gray, 95.0, 255.0, cv2.THRESH_BINARY)
cv2.imshow("image_binary", image_binary)

# 查找轮廓
# 返回轮廓集合，轮廓层级关系
contours, hierarchy = cv2.findContours(image_binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

# 遍历轮廓
for contour in contours:
    # print(contour.shape)#(n, r, c)
    contour_area = cv2.contourArea(contour)
    if contour_area > 200:
        # 寻找凸包,返回(n,r,c) <=> n个r行c列的数组
        hull1 = cv2.convexHull(contour, returnPoints=True)
        # 遍历凸包的所有点，绘制
        for hull in hull1:
            # print(hull)
            x = hull[0][0]
            y = hull[0][1]
            cv2.circle(image_bgr, (x, y), 5, (255, 0, 0), 2)
        cv2.imshow("image_bgr", image_bgr)
        # cv2.waitKey()
        
        # 寻找凸包；返回(n, index) <=> n个点在contour中的索引
        hull2 = cv2.convexHull(contour, returnPoints=False)
        # print(hull2)

        # 绘制凸包
        cv2.polylines(image_bgr, [hull1], True, (0, 0, 255), 2)
        
        # 描述凸缺陷的四个特征值(1.起点索引值；2.终点索引值；3.轮廓上离凸包最远的点；4.最远点到凸包的近似距离)
        # 获取凸缺陷；返回多个特征值
        convexity_defects = cv2.convexityDefects(contour, hull2)
        # print(convexity_defects.shape)
        
        # 遍历凸缺陷
        convexity_defect_index = 0
        valid_convexity_defect_count = 0
        for convexity_defect in convexity_defects:
            # print(convexity_defect)
            start_index = convexity_defect[0][0]
            end_index = convexity_defect[0][1]
            farst_index = convexity_defect[0][2]
            print("start_index=%d,end_index=%d,farst_index=%d" % (start_index, end_index, farst_index))
            
            # print(contour[start_index])
            start_x, start_y = contour[start_index][0]
            # print(start_x, start_y)
            end_x, end_y = contour[end_index][0]
            # print(end_x, end_x)
            farst_x, farst_y = contour[farst_index][0]
            # print(farst_x, farst_y)
            
            area = triangle_area((start_x, start_y), (end_x, end_y), (farst_x, farst_y))
            if area > 3000:
                cv2.circle(image_bgr, (int(start_x), int(start_y)), 3, (0, 255, 0), 2)
                cv2.circle(image_bgr, (int(end_x), int(end_y)), 3, (0, 255, 0), 2)
                cv2.circle(image_bgr, (int(farst_x), int(farst_y)), 3, (0, 255, 0), 2)
                
                valid_convexity_defect_count = valid_convexity_defect_count + 1
            
            convexity_defect_index = convexity_defect_index + 1
        
        # 打印凸缺陷个数
        print("valid_convexity_defect_count=%ld" % valid_convexity_defect_count)

cv2.imshow("image_bgr", image_bgr)
cv2.waitKey()

cv2.destroyAllWindows()
exit(0)